<template>
	<view class="musicplayer" v-if="musicPlaylist.length > 0">
		<!-- TopBar组件 -->
		<top-bar iconColor="black"></top-bar>
		<image class="music-image" v-if="curMusic.al" :src="curMusic.al.picUrl" lazy-load></image>
		<view class="bottom">
			<view class="song_name">{{curMusic.name}}</view>
			<view class="singer" v-if="curMusic.ar">{{curMusic.ar[0].name}}</view>
			<view v-show="!lookLyrics">
				<!-- 播放杆子 -->
				<view class="play_pole">
					<image @click="MusicActived" :class="isPlay?'actived':''" src="../../static/images/song/needle.png" mode="widthFix"></image>
				</view>
				<!-- 播放转盘 -->
 				<!--<view class="play_turntable" @click="checkLyrics" :class="isPlay?'zhuanpan_actived':'zhuanpan_paused zhuanpan_actived'">
					<image class="zhuanpan" src="../../static/images/song/disc.png" mode="widthFix"></image>
					<image class="zhuanpan_in_img" :src="curMusic.al.picUrl"></image>
				</view> -->
				<view class="swiper_box">
					<view class="mask"></view>
					<swiper circular @click="checkLyrics" :duration="300" :current="swiperIndex" @animationfinish="slideChangeMusic">
						<swiper-item v-for="(item, index) in swiperList" :key="index">
							<!-- 播放转盘 -->
							<view class="play_turntable" :class="isPlay?'zhuanpan_actived':'zhuanpan_paused zhuanpan_actived'">
								<image class="zhuanpan" src="../../static/images/song/disc.png" mode="widthFix"></image>
								<image class="zhuanpan_in_img" v-if="curMusic.al" :src="curMusic.al.picUrl ? curMusic.al.picUrl : item.al.picUrl"></image>
							</view>
						</swiper-item>
					</swiper>		
				</view>
			</view>
			<!-- 歌词区域 -->
			<view class="lyrics" @click="closeLyrics" v-if="lookLyrics">
				<!-- scroll-into-view 根据子元素的id值来定位,子元素的id值不能以数字开头 -->
				<!-- 下面是对计算属性 scrollIntoView 的 逻辑解释 -->
				<!--  a. 减5的原因是让其等待等 curSiteIndex等于5时,才定位到歌词的第一行[即歌词第一行到滚动的顶部] -->
				<!--  b. 这样就实现了当前实时歌词在可见歌词的第五行,而不再第一行的效果 -->
				<!--  c. 当curSiteIndex等于0时,把scroll-into-view='start0'原因是歌曲切换时,让歌词列表当前定位行先自动回到顶部 -->
				<scroll-view scroll-y="true" :scroll-into-view="scrollIntoView">
					<view class="empy" id="start0"></view>
					<view class="empy"></view>
					<view class="empy"></view>
					<view class="empy"></view>
					<view class="lyric-item" :class="curSiteIndex - 1 === index?'cur_style':''" :id="'site' + index" v-for="(lyric, index) in lyrics" :key="index">{{lyric.text}}</view>
					<view class="empy"></view>
					<view class="empy"></view>
					<view class="empy"></view>
				</scroll-view>
			</view>
			<view class="footer_fix">
				<!-- 底部收藏添加评论 -->
				<view class="bom_tool">
					<view><text class="iconfont icon-xihuan-xianxing"></text></view>
					<view><text class="iconfont icon-jia"></text></view>
					<view><text class="iconfont icon-wodexiaoxi"></text></view>
				</view>
				<!-- 进度条区域 -->
				<view class="music_slider">
					<view>{{curDurationTime}}</view>
					<slider @changing="draggingSlider" @change="dragSlider" class="music-slider-item" :max="durationTimeSecond" block-size="12" activeColor="#FF0000" :value="curTime"
						backgroundColor="#FFFFFF"></slider>
					<view>{{getDurationTime()}}</view>
				</view>
				<!-- 播放按钮区域 -->
				<view class="five-icon">
					<view> <text class="iconfont icon-yuanxunhuanbofang-copy"></text></view>
					<view>
						<text class="iconfont icon-icon-1" @click="preNextSong(true)" id="pre"></text>
					</view>
					<view>
						<text class="iconfont big" :class="musicIsPlay" @click="MusicActived"></text>
					</view>
					<view>
						<text class="iconfont icon-icon-" @click="preNextSong(false)"  id="next"></text>
					</view>
					<view @click="showMenu">
						<text class="iconfont icon-caidan"></text>
					</view>
				</view>
			</view>
		</view>
		<Menu @musicPlay="musicPlay" @showMenu="showMenu" :isShowMenu="isShowMenu"></Menu>
	</view>
</template>

<script>
	import moment from "moment"
	import { mapState } from "vuex"
	import { getMusicUrl, getSongdetail, getLyrics } from "../../api/index.js"
	import TopBar from '../../components/TopBar.vue'
	import Menu from '../../components/Menu/Menu.vue'
	// // 获取全局唯一的背景音频管理器[不支持H5]
	// // #ifndef H5
	// const musicPlay = uni.getBackgroundAudioManager()
	// // #endif
	// // #ifdef H5
	// const musicPlay = uni.createInnerAudioContext()
	// musicPlay.autoplay = true
	// // #endif
	// // 定时器的唯一标识值
	// let timer
	// 全局用于判断的当前播放歌曲的index值
	let judegIndex = 0
	export default {
		data() {
			return {
				// 当前播放音乐的obj
				curMusic: {},
				// 当前音乐索引值[原数据当前项]
				curIndex: uni.getStorageSync('curIndex'),
				// 音乐本地播放列表
				musicPlaylist: uni.getStorageSync('musicPlaylist') || [],
				// 当前播放歌曲的总时长[毫秒]
				durationTime: 0,
				// 当前播放歌曲的详情
				songDetail: {},
				// 歌词及其实时时间
				lyrics: [],
				// 是否查看歌词
				lookLyrics: false,
				// 当前歌词位置【默认在第一行】
				curSiteIndex: 0,
				// flag 用于判断用户点击的是上一曲还是下一曲
				flag: false,
				// swiper当前项索引
				swiperIndex: uni.getStorageSync('swiperIndex') || 0,
				// 轮播切换歌曲的歌曲列表
				swiperList: uni.getStorageSync('swiperList'),
				// 是否显示菜单
				isShowMenu: false,
				// 是否是第一次或再次进入该页面
				isFlag: true
			}
		},
		components: { TopBar, Menu },
		onLoad ({ id, cur_index, flag}) {
			// 设置全局用于判断的index值
			judegIndex = cur_index-0
			// this.swiperIndex = judegIndex
			// 更新vue中的当前播放歌曲的索引值
			this.$store.commit('CURRENTINDEX', judegIndex)
			// flag: 为1,不论用户点击的音乐索引值与本地音乐索引值是否相等都会触发,0表示索引值不相等再进入
			if (judegIndex != this.curIndex || flag != 0) {
				// 说明播放的不是同一首音乐
				// 1.更新本地的索引值
				// 2.更新data中的当前播放音乐的索引值
				// 3.重置进度条进度
				// 4. getBackgroundAudioManager 现在是自动播放，createInnerAudioContext我设置了自动播放
				// 5. 索引值一样，说明是同一首歌曲，不用再重新加载监听播放事件
				// 6. 如果再其他页面是播放或暂停状态，那么进入该页面也是相应的状态
				// 7. 不一样播放音乐
				uni.setStorageSync('curIndex', judegIndex)
				this.curIndex = judegIndex
				this.$store.commit('RESETTIME')
				// 不是同一首再执行获取音乐url设置音频播放器的监听函数及属性设置,防止资源浪费
				this.getMusicUrlFun(id)
				// 初始化swiperList 数据
				this.initSwiperData()
        // #ifndef APP
				// 播放
				this.$musicPlay.play()
        // #endif
			}
			this.curMusic = this.musicPlaylist[cur_index]
			this.getSongdetailFun(id)
			this.getLyricsFun(id)
			/**
			 * 注意：  1. 同时使用 stop() 和 pause() 会有冲突
			 * 		  2. 在触发stop()后，再触发pause(),onPause()监听事件会失效
			 * 		  3. 必须再触发一次pause(),onPause()才会再次触发。
			 * */ // this.$musicPlay.onStop(() => {})
			// 监听歌曲进度更新回调
			this.$musicPlay.onTimeUpdate(() => {
				this.handleLyricTrack()
			})
		},
		computed:{
			...mapState(['isPlay', 'timer', 'curTime', 'isEnded']),
			musicIsPlay() {
				return this.isPlay? 'icon-zanting' : 'icon-icon-2'
			},
			// 设置音乐进度条的最大值	秒
			durationTimeSecond() {
				return Math.floor(this.durationTime / 1000)
			},
			// 进度条进度时间
			curDurationTime() {
				return moment(this.curTime * 1000).format("mm:ss")
			},
			scrollIntoView() {
				return this.curSiteIndex === 0?'start0':`site${this.curSiteIndex - 5}`
			}
		},
		watch: {
			isEnded() {
				this.newMusicIndex('next')
			}
		},
		methods: {
			MusicActived() {
			    // 判断是否正在播放
			    if (!this.isPlay) {
					if (judegIndex === this.curIndex) {
						// 设置音频播放的位置等于上一次停止的位置
						this.$musicPlay.startTime = this.$musicPlay.currentTime
						// 再获取音乐url和设置监听onPlay()
						this.getMusicUrlFun(this.musicPlaylist[this.curIndex].id)
					} else {
						this.$musicPlay.play();
					}
			    } else {
					this.$musicPlay.pause();
			    }
				// 取反[实现暂停播放]
				this.$store.commit('PLAYPUASE', !this.isPlay)
			},
			musicPlay(index) {
				console.log(index)
				this.$musicPlay.pause()
				// 清除定时器
				clearInterval(this.timer)
				this.$store.commit('RESETTIME')
				this.complyPlay(index)
				// 更新本地的当前Index值
				uni.setStorageSync('curIndex', index)
			},
			showMenu() {
				this.isShowMenu = !this.isShowMenu
			},
			complyPlay(index) {
				// 1. 更新播放歌曲的obj
				// 2. 实时更新data数据对象中的 当前索引值
				// 3.根据最新的索引值获取最新的歌曲id,获取最新的歌曲数据进行播放
				// 4.更新歌词
				// 5.重置音乐歌词当前索引值
				this.curSiteIndex = 0
				this.curIndex = index
				this.curMusic = this.musicPlaylist[this.curIndex]
				const id = this.curMusic.id
				this.getMusicUrlFun(id)
				this.getSongdetailFun(id)
				this.getLyricsFun(id)
			},
			// 点击上一曲图标后触发 / 点击下一曲图标后触发
			/**
			 * 1.上一曲相对于当前音乐的索引值减1,下一曲则加1
			 * 2.根据索引值得到相应音乐的id
			 * 3.重新获取音乐url及详情
			 * 4.参数 flag为true则上一曲,为false则下一曲
			 * */
			preNextSong(flag) {
				if (flag) {
					// 更新用户点击是上一曲还是下一曲状态
					this.flag = true
					this.newMusicIndex('pre')
				} else {
					// 更新用户点击是上一曲还是下一曲状态
					this.flag = false
					this.newMusicIndex('next')
				}
			},
			newMusicIndex(type) {
				// 延时500毫秒
				setTimeout(async () => {
					// 先暂停上一曲的播放,重置进度条为0重置进度条为0
					// 本来这里用的是stop(),但是该方法后 opPause() 与 onTimeUpdate()就不在触发
					// 得用些特殊手段才能导致再次触发[比如再触发或改变音乐进度等],什么原因不得而知
					// 暂停播放
					this.$musicPlay.pause()
					// 清除定时器
					clearInterval(this.timer)
					this.$store.commit('RESETTIME')
					const curIndex = await this.$store.dispatch('changeCurrentIndex', { type })
					this.complyPlay(curIndex)
				}, 300)	
			},
			// 获取歌曲详情[内含时长]的方法
			async getSongdetailFun(id) {
				const {data: {songs}} = await getSongdetail(id)
				// 获取当前播放的歌曲详情
				this.songDetail = songs[0]
				// 获取当前播放的歌曲时长
				this.durationTime = songs[0].dt
				this.$store.commit('DURATIONTIMESECOND', this.durationTime)
			},
			// 获取歌曲url的方法
			async getMusicUrlFun (id) {
				// 当前播放的音乐url
				let curMusicUrl = ''
				const {data: {data: res}} = await getMusicUrl(id)
				curMusicUrl = res[0].url
				// #ifndef H5
				this.$musicPlay.title = this.curMusic.name
				// #endif
				// 监听播放的音乐url变化,实时更新当前播放的音乐
				if (curMusicUrl == null) {
					console.log('asa121321')
					uni.showToast({
						title: '抱歉, 暂时无法播放哦!',
						icon: 'error',
						mask: true,
						duration: 1000
					})
					// 得到源数据的长度
					const originListLength = this.musicPlaylist.length
					// 自动切换下一首/上一首
					if (this.flag) {
						// url不存在,再次改变swiper的下标值,再进行上曲[由于swiperIndex改变,会触发slideChangeMusic,但由于最新的swiper下标等于this.swiperIndex,故if else if 语句判断不走]
						this.swiperIndex = this.swiperIndex - 1 == -1 ? 2 : this.swiperIndex - 1
						// 故不会走initSwiperData函数,即不会走上一曲下一曲逻辑,得手动触发
						this.newMusicIndex('pre') 
					} else {
						// url不存在,再次改变swiper的下标值,再进行下曲[由于swiperIndex改变,会触发slideChangeMusic,但由于最新的swiper下标等于this.swiperIndex,故if else if 语句判断不走]
						this.swiperIndex = this.swiperIndex + 1 == 3 ? 0 : this.swiperIndex + 1
						// 故不会走initSwiperData函数,即不会走上一曲下一曲逻辑,得手动触发
						this.newMusicIndex('next')
					}
					// this.flag?this.newMusicIndex('pre') : this.newMusicIndex('next')
				} else {
					// 有值则赋值播放
					this.$musicPlay.src = curMusicUrl
				}
			},
			async getLyricsFun (id) {
				const {data: {lrc: {lyric: res}}} = await getLyrics(id)
				// 获取到以字符串格式的实时时间和歌词
				const lyric = res
				// \n 切割为数据
				let arr = lyric.split('\n')
				// 存储歌词实时时间和歌词
				let result = []
				// 查找数据中的空字符进行过滤
				let resultArr = []
				arr.forEach(item => {
					if (item != '') {
						resultArr.push(item)
					}
				})
				// 分别得到歌词实时时间和歌词
				resultArr.forEach(item => {
					// 歌词实时时间[time中会多一个 '[', 故要进行二次分割 ]
					let time = item.split(']')[0]
					let lyricTime = time.slice(1).split(':')
					// 歌词文本
					let lyricTxt = item.split(']')[1]
					result.push({
						// 如果字符串以0开头，将其按照10进制解析 可以简写为 parseInt('02') 2
						// parseFloat('00.56') 解析为 0.56 小数点前面一个0有效,其余的去掉
						time: parseInt(lyricTime[0], 10) * 60 + parseFloat(lyricTime[1]),
						text: lyricTxt
					})
				})
				// 处理完毕
				// 存入data
				this.lyrics = result
			},
			getDurationTime() {
				return moment(this.durationTime).format("mm:ss");
			},
			// 点击转盘查看歌词
			checkLyrics() {
				this.lookLyrics = true
			},
			// 点击歌词列表任何区域关闭歌词列表
			closeLyrics() {
				this.lookLyrics = false
			},
			// 进度条 完成一次拖动后触发的事件
			async dragSlider(e) {
				// 停止 音乐的播放
				// [async/await] await 后面不是promise对象会先执行async函数外的同步代码,
				// 在回到async函数中把 await 后的 非promise的东西作为await表达式的结果,
				// 这样可以起到阻挡await后面代码的执行的作用
				await this.$musicPlay.pause()
				// 获取拖拽进度条后的时间点[秒]
				const dragAfterTime = e.detail.value
				/** 实现歌词根据用户拖动进度条到指定歌词位置 */
				this.lyrics.forEach((v, i) => {
					if (dragAfterTime >= v.time) {
						this.curSiteIndex = i
					}
				})
				/*  */
				// 设置音乐开始播放的时间节点
				// this.$musicPlay.startTime = dragAfterTime
				// 	跳转到指定位置[用户滑动后的时间节点]，单位 s
				this.$musicPlay.seek(dragAfterTime)
				// 更新vuex 中存储的 音乐当前已播时间值
				this.$store.commit('RENEWCURTIME', dragAfterTime)
				// 重新播放
				this.$musicPlay.play()
			},
			// 进度条 拖动过程中触发的事件
			draggingSlider(e) {
				// 清除定时器
				clearInterval(this.timer)
				// 获取拖拽进度条后的时间点[秒]
				const dragAfterTime = e.detail.value
				// 更新vuex 中存储的 音乐当前已播时间值
				this.$store.commit('RENEWCURTIME', dragAfterTime)
			},
			// 进度条 跟踪 歌词
			// 处理歌词跟踪
			handleLyricTrack() {
				// 1. 得到当前音频的播放位置（单位：s）,默认保留6位小数,切为3位,toFixed转为字符类型
				// 2. 判断音乐歌词当前索引值 是否小于歌词数组的长度
				// 3.小于则再判断当前音频的播放位置（单位：s）是否大于等于 歌词实时的时间节点
				// 4.满足条件后则将音乐歌词当前索引值++
				const currentTime = parseFloat((this.$musicPlay.currentTime).toFixed(3))
				if (this.curSiteIndex < this.lyrics.length) {
					// console.log('#￥#'+currentTime, this.lyrics[this.curSiteIndex].time)
					if (currentTime >= this.lyrics[this.curSiteIndex].time) {
						this.curSiteIndex++
					}
				}
			},
			initSwiperData(curIndex = this.curIndex) {
				// 得到源数据的长度
				const originListLength = this.musicPlaylist.length
				// 初始化一个 swiper数组
				const swiperList = []
				swiperList[this.swiperIndex] = this.musicPlaylist[curIndex]
				swiperList[this.swiperIndex - 1 == -1 ? 2 : this.swiperIndex -1] = 
				this.musicPlaylist[curIndex - 1 == -1 ? originListLength - 1 : curIndex - 1]
				swiperList[this.swiperIndex + 1 == 3 ? 0 : this.swiperIndex + 1] =
				this.musicPlaylist[curIndex + 1 == originListLength ? 0 : curIndex + 1]
				this.swiperList = swiperList
				if (!this.isFlag) {
					this.flag ? this.newMusicIndex('pre') : this.newMusicIndex('next')
				}
				uni.setStorageSync('swiperList', this.swiperList)
				uni.setStorageSync('swiperIndex', this.swiperIndex)
			},
			// 当swiper动画结束时执行
			slideChangeMusic(e) {
				this.isFlag = false
				// 得到swiper当前项索引值
				const { current } = e.detail
				// 得到源数据的长度
				const originListLength = this.musicPlaylist.length
				// 向后
				if (this.swiperIndex - current == 2 || this.swiperIndex - current == -1) {
					this.flag = false
					this.curIndex = this.curIndex + 1 == originListLength ? 0:this.curIndex + 1
					this.swiperIndex = this.swiperIndex + 1 == 3 ? 0 : this.swiperIndex + 1
					this.initSwiperData(this.curIndex)
				} else if (this.swiperIndex - current == -2 || this.swiperIndex - current == 1) {
					this.flag = true
					this.curIndex = this.curIndex - 1 == -1 ? originListLength - 1 : this.curIndex - 1
					this.swiperIndex = this.swiperIndex - 1 == -1 ? 2 : this.swiperIndex - 1
					this.initSwiperData(this.curIndex)
				}
				// 更新本地的swiperIndex
				uni.setStorageSync('swiperIndex', this.swiperIndex)
			}
		}
	}
</script>

<style lang="scss">
	.musicplayer {

		background-color: rgba(0, 0, 0, 0.3);
		width: 750rpx;
		height: 100%;
		// 高斯模糊图片设置为musicplayer的子元素，设置为musicplayer的背景图会遮挡内容，只能看到模糊后的图片
		.music-image {
			// 高斯模糊效果
			width: 100%;
			height: 100%;
			filter: blur(40rpx);
			opacity: .8;
			position: fixed;
			left: 0;
			right: 0;
			top: 0;
			bottom: 0;
			z-index: -1;
		}

		// box-shadow: 10rpx 10rpx 20rpx #FFFFFF inset;

		.bottom {
			text-align: center;
			color: white;
			// 歌名
			.song_name {
				font-weight: bold;
			}
			// 歌手
			.singer {
				font-weight: 500;
				margin-top: 15rpx;
			}
			// 歌词区域
			.lyrics {
				margin-top: 20rpx;
				margin-bottom: 10rpx;
				padding: 0 10rpx;
				height: 70vh;
				overflow: hidden;
				scroll-view {
					height: 56.8vh;
					.empy {
						width: 100%;
						min-height: 42rpx;
						margin: 40rpx 0;
					}
					.lyric-item {
						font-size: 30rpx;
						line-height: 90rpx;
						color: #ffffff;
						&.cur_style {
							color: red;
						}
					}
				}
			}
			// 播放杆子区域
			.play_pole {
				width: 35vw;
				height: 35vw;
				margin: 0 auto;
				position: relative;
				z-index: 50;
				transform: translateX(45%);
				margin-top: 35rpx;
				image {
					width: 100%;
					transition: transform 1s;
					transform-origin: 14rpx 14rpx;
					&.actived {
						transform: rotate(23deg);
					}
				}
			}
			// 播放转盘动画
			.zhuanpan_actived {
				// 动画名
				animation-name: zhuanpan;
				// 动画持续时间
				animation-duration: 10s;
				// 动画持续次数
				animation-iteration-count: infinite;
				// 动画速度曲线
				animation-timing-function: linear;
				// 动画延迟
				animation-delay: .8s;
				// 动画填充模式
				animation-fill-mode: forwards;
			}
			// 停止动画样式
			.zhuanpan_paused {
				animation-play-state: paused;
			}
			// 转盘动画关键帧
			@keyframes zhuanpan {
				from {
					transform: rotate(0deg);
				}
				to {
					transform: rotate(360deg);
				}
			}
			.swiper_box {
				position: relative;
				// 播放转盘下的半透明圆背景
				.mask {
					position: absolute;
					left: 50%;
					transform: translate(-50%, -14.2%);
					width: 72vw;
					height: 72vw;
					background-color: rgba(255, 255, 255, .3);
					border-radius: 50%;
				}
				swiper {
					width: 100vw;
					height: 70vw;
					transform: translateY(-13%);
					// 转盘样式
					.play_turntable {
						width: 70vw;
						height: 100%;
						margin: 0 auto;
						.zhuanpan {
							width: 100%;
						}
						// 转盘内的图片样式
						.zhuanpan_in_img {
							position: absolute;
							left: 15%;
							top: 15%;
							width: 70%;
							height: 70%;
							border-radius: 50%;
						}
					}
				}
			}
			// 底部工具样式
			.footer_fix {
				position: absolute;
				bottom: 50rpx;
				width: 750rpx;
				.bom_tool {
					display: flex;
					justify-content: space-around;
				
					text {
						font-size: 50rpx;
					}
				
					view:nth-child(1) {
						margin-left: 60rpx;
					}
				
					view:nth-child(3) {
						margin-right: 60rpx;
					}
				}
				// 进度条样式
				.music_slider {
					display: flex;
					margin: 40rpx auto;
					width: 92%;
					height: 50rpx;
					line-height: 50rpx;
					font-size: 30rpx;
					color: #fff;
					// 进度条【在设置flex后其宽度为0】
					.music-slider-item {
						width: 84%;
						margin: auto 20rpx;
					}
				}
				// 上下曲/播放停止
				.five-icon {
					display: flex;
					align-items: center;
					view {
						flex: 1;
						text-align: center;
						text {
							font-size: 50rpx;
							&.big {
								font-size: 80rpx;
							}
						}
					}
				}
			}
		}
	}
</style>
